package com.tian.service.impl;

import com.alibaba.fastjson.JSON;
import com.tian.common.CommonResult;
import com.tian.dto.exchange.ExchangeCodeDto;
import com.tian.entity.ChargeCoupon;
import com.tian.entity.ExchangeCode;
import com.tian.enums.ChargeCouponConditionExpireUnitEnum;
import com.tian.enums.CouponStatusEnum;
import com.tian.enums.ExchangeCodeStatusEnum;
import com.tian.enums.ResultCode;
import com.tian.exception.BusinessException;
import com.tian.mapper.ChargeCouponMapper;
import com.tian.mapper.ExchangeCodeMapper;
import com.tian.service.ExchangeCodeService;
import com.tian.util.ChargeMarketRedisKeyPrefix;
import com.tian.util.CodeUtil;
import com.tian.util.RedisConstantPre;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * {@code @description:} 兑换码服务实现类
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-11-13 9:46
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class ExchangeCodeServiceImpl implements ExchangeCodeService {
    @Resource
    private ExchangeCodeMapper exchangeCodeMapper;
    @Resource
    private ChargeCouponMapper chargeCouponMapper;
    @Resource
    private RedissonClient redissonClient;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommonResult<Boolean> generateCode(Integer couponId, Integer count) {
        // 查询优惠券信息
        ChargeCoupon record = chargeCouponMapper.selectByPrimaryKey(couponId);
        if (record == null) {
            return CommonResult.failed();
        }

        Date expire;
        // 计算优惠券过期时间
        if (record.getValidityType() == 1) {
            Integer expireType = record.getExpire();
            Integer expireUnit = record.getExpireUnit();

            LocalDate expireDate = ChargeCouponConditionExpireUnitEnum.calculateExpireDate(LocalDate.now(), expireUnit, expireType);
            if (expireDate == null) {
                log.info("优惠券id:{}，优惠券过期时间计算失败", couponId);
                return CommonResult.failed(ResultCode.PARAMETER_ERROR);
            }
            expire = Date.from(Timestamp.valueOf(expireDate.atTime(LocalTime.MIDNIGHT)).toInstant());
        } else {
            if (record.getValidityEndTime().before(new Date())) {
                log.info("优惠券id:{}，优惠券过期时间计算失败", couponId);
                return CommonResult.failed("优惠券已过期，不能再生成兑换编码！");
            }
            expire = record.getValidityEndTime();
        }
        //获取优惠券序列号
        long serialNum = redissonClient.getAtomicLong(ChargeMarketRedisKeyPrefix.EXCHANGE_CODE_COUNT_KEY).addAndGet(count);
        List<ExchangeCode> exchangeCodeList = new ArrayList<>();
        log.info("优惠券id:{}，生成兑换码数量:{}", couponId, count);
        // 生成兑换码
        for (long i = serialNum - count + 1; i <= serialNum; i++) {
            ExchangeCode exchangeCode = new ExchangeCode();
            exchangeCode.setCode(CodeUtil.generateCode(i, couponId));
            exchangeCode.setCouponId(couponId);
            exchangeCode.setStatus(ExchangeCodeStatusEnum.NOT_USED.getCode());
            exchangeCode.setUpdateTime(new Date());
            exchangeCode.setExpiredTime(expire);
            exchangeCode.setCreateTime(new Date());
            exchangeCodeList.add(exchangeCode);
        }
        exchangeCodeMapper.insertBatch(exchangeCodeList);
        // 缓存兑换码信息到redis
        for (ExchangeCode exchangeCode : exchangeCodeList) {
            ExchangeCodeDto exchangeCodeDto = new ExchangeCodeDto();
            BeanUtils.copyProperties(exchangeCode, exchangeCodeDto);
            redissonClient.getBucket(RedisConstantPre.EXCHANGE_CODE_KEY + exchangeCode.getCode()).set(JSON.toJSONString(exchangeCodeDto));
        }
        // 更新优惠券状态为已生成兑换码
        record.setStatus(CouponStatusEnum.GENERATED.getStatus());
        chargeCouponMapper.updateByPrimaryKey(record);
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<Boolean> validateCode(String code) {
        // 校验兑换码格式
        if (!CodeUtil.validateCode(code)) {
            return CommonResult.failed("兑换码格式不正确！");
        }
        RBucket<Object> bucket = redissonClient.getBucket(RedisConstantPre.EXCHANGE_CODE_KEY + code);
        ExchangeCodeDto exchangeCodeDto = JSON.parseObject(bucket.get().toString(), ExchangeCodeDto.class);
        if (exchangeCodeDto == null) {
            ExchangeCode exchangeCode = exchangeCodeMapper.selectByCode(code);
            if (exchangeCode == null) {
                // 兑换码不存在，设置空缓存（避免恶意攻击 缓存数据库都不存在的情况）
                bucket.set(JSON.toJSONString(new ExchangeCodeDto()));
            }
            return CommonResult.failed("兑换码不存在！");
        }

        if (exchangeCodeDto.getStatus() == null) {
            return CommonResult.failed("兑换码不存在！");
        }
        ExchangeCode exchangeCode = exchangeCodeMapper.selectByCode(code);
        if (exchangeCode == null) {
            return CommonResult.failed("兑换码不存在！");
        }
        if (Objects.equals(exchangeCode.getStatus(), ExchangeCodeStatusEnum.USED.getCode())) {
            throw new BusinessException("兑换码已使用");
        }
        if (Objects.equals(exchangeCode.getStatus(), ExchangeCodeStatusEnum.USING.getCode())) {
            throw new BusinessException("兑换码正在使用");
        }
        if (Objects.equals(exchangeCode.getStatus(), ExchangeCodeStatusEnum.EXPIRED.getCode())) {
            throw new BusinessException("兑换码已过期");
        }
        if (exchangeCode.getExpiredTime().before(new Date())) {
            return CommonResult.failed("兑换码已过期！");
        }
        return CommonResult.success(Boolean.TRUE);
    }
}
